package com.cable.utils.db;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 该类有三种处理方式：
 *
 * <p>1.直接返回插入列表、更新列表和删除列表，由使用者自己处理（get）
 *
 * <p>2.自动处理数据，其中删除列表将硬删（refreshWithHardDelete）
 *
 * <p>3.自动处理数据，其中删除列表将为软删（refreshWithSoftDelete）
 *
 * <p>这里要处理两种模式，一种是带key，一种是不带key
 */
@Slf4j
public class DBRefreshUtils {

	public static <DB, KEY_DB, ID> DataWrapper<KEY_DB, DB> db(
		List<DB> list,
		FieldUtils.FieldGetter<DB, ID> idFieldGetter,
		Function<DB, KEY_DB> keyExtractFunction) {
		Field idField = FieldUtils.getField(idFieldGetter);
		if (idField == null) {
			throw new RuntimeException("缺少主键");
		}
		DataClassify<DB> dataClassify = new DataClassify();
		Map<KEY_DB, DB> dbMap =
			list.stream()
			    .collect(
				    LinkedHashMap::new,
				    (m, v) -> {
					    if (v == null) {
						    return;
					    }
					    KEY_DB key = keyExtractFunction.apply(v);
					    if (ObjectUtil.isNotNull(key)) {
						    // 如果存在重复就让他报错
						    if (m.containsKey(key)) {
							    dataClassify.getDbRepeatList().add(v);
						    } else {
							    m.put(key, v);
						    }
					    }
				    },
				    LinkedHashMap::putAll
			    );

		return new DataWrapper(idField, dbMap, keyExtractFunction, dataClassify);
	}

	@AllArgsConstructor
	public static class DataWrapper<KEY_DB, DB> {

		private Field idField;

		private Map<KEY_DB, DB> dbMap;

		private Function<DB, KEY_DB> dbKeyExtractFunction;

		private DataClassify<DB> dataClassify;

		public ExecuteWrapper<KEY_DB, KEY_DB, DB, DB> data(List<DB> list) {
			return data(list, this.dbKeyExtractFunction);
		}

		public <DATA, KEY_DATA> ExecuteWrapper<KEY_DB, KEY_DATA, DATA, DB> data(
			List<DATA> list, Function<DATA, KEY_DATA> keyExtractFunction) {
			Map<KEY_DATA, DATA> dataMap =
				list.stream()
				    .collect(
					    LinkedHashMap::new,
					    (m, v) -> {
						    if (v == null) {
							    return;
						    }
						    KEY_DATA key = keyExtractFunction.apply(v);
						    if (ObjectUtil.isNotNull(key)) {
							    // 如果存在重复就让他报错
							    if (m.containsKey(key)) {
								    //dataClassify.getDataRepeatList().add(v);
							    } else {
								    m.put(key, v);
							    }
						    }
					    },
					    LinkedHashMap::putAll
				    );

			return new ExecuteWrapper(idField, this.dbMap, dataMap, this.dataClassify);
		}

	}

	public static class ExecuteWrapper<KEY_DB, KEY_DATA, DATA, DB> {

		private Field idField;

		private Map<KEY_DB, DB> dbMap;

		private Map<KEY_DATA, DATA> dataMap;

		private Set<KEY_DB> dbKeyList;

		private Set<KEY_DATA> dataKeyList;

		private Function<DATA, DB> insertFunction = item -> (DB) item;

		private BiFunction<DB, DATA, DB> updateBiFunction;

		private DataClassify<DB> dataClassify;

		public ExecuteWrapper(Field idField, Map<KEY_DB, DB> dbMap, Map<KEY_DATA, DATA> dataMap, DataClassify<DB> dataClassify) {
			this.idField = idField;
			this.dbMap = dbMap;
			this.dataMap = dataMap;
			this.dbKeyList = this.dbMap.keySet();
			this.dataKeyList = this.dataMap.keySet();
			this.dataClassify = dataClassify;
		}

		public ExecuteWrapper<KEY_DB, KEY_DATA, DATA, DB> insert(
			Function<DATA, DB> insertFunction) {
			this.insertFunction = insertFunction;
			return this;
		}

		public ExecuteWrapper<KEY_DB, KEY_DATA, DATA, DB> update(
			BiFunction<DB, DATA, DB> updateBiFunction) {
			this.updateBiFunction = updateBiFunction;
			return this;
		}

		public DataClassify<DB> get() {
			// dataList有的、dbList没有的，则插入
			List<DB> insertList = getInsert();

			// dataList没有的、dbList有的，则删除
			List<DB> deleteList = getDelete();

			// dataList有的、dbList有的，则更新
			List<DB> updateList = getUpdate();

			dataClassify.setInsertList(insertList);
			dataClassify.setUpdateList(updateList);
			dataClassify.deleteList = deleteList;
			dataClassify.setDeleteIdList(getIdsFromDeletes(deleteList, idField));

			return dataClassify;
		}

		//public <S extends IBaseService> void executeWithHardDelete(S service) {
		//	DataClassify<DB> dataClassify = get();
		//
		//	if (CollUtil.isNotEmpty(dataClassify.getInsertList())) {
		//		service.saveBatch(dataClassify.getInsertList());
		//	}
		//
		//	if (CollUtil.isNotEmpty(dataClassify.getDeleteIdList())) {
		//		service.getBaseMapper().deleteBatchIds(dataClassify.getDeleteIdList());
		//	}
		//
		//	if (CollUtil.isNotEmpty(dataClassify.getUpdateList())) {
		//		service.updateBatchById(dataClassify.getUpdateList());
		//	}
		//}

		//public <S extends IBaseService, SOFT_DELETE_KEY> void executeWithSoftDelete(
		//	S service, FieldUtils.FieldGetter<DB, SOFT_DELETE_KEY> softDeleteKeyFieldGetter) {
		//	DataClassify<DB> dataClassify = get();
		//
		//	if (CollUtil.isNotEmpty(dataClassify.getInsertList())) {
		//		service.saveBatch(dataClassify.getInsertList());
		//	}
		//
		//	List<DB> updateList =
		//		new ArrayList<>(
		//			dataClassify.getDeleteIdList().size()
		//				+ dataClassify.getUpdateList().size());
		//	if (CollUtil.isNotEmpty(dataClassify.deleteList)) {
		//		Field field = FieldUtils.getField(softDeleteKeyFieldGetter);
		//		dataClassify.deleteList.forEach(item -> ReflectUtil.setFieldValue(item, field, -1));
		//
		//		updateList.addAll(dataClassify.deleteList);
		//	}
		//
		//	updateList.addAll(dataClassify.getUpdateList());
		//	if (CollUtil.isNotEmpty(updateList)) {
		//		service.updateBatchById(updateList);
		//	}
		//}

		private List<Long> getIdsFromDeletes(List<DB> deleteList, Field idField) {
			return deleteList.stream()
			                 .map(item -> ReflectUtil.getFieldValue(item, idField))
			                 .distinct()
			                 .filter(item -> ObjectUtil.isNotNull(item))
			                 .map(item -> (Long) item)
			                 .collect(Collectors.toList());
		}

		// data中有，但db中没有，并需要将data转成db
		private List<DB> getInsert() {
			return this.dataKeyList.stream()
			                       .filter(item -> !this.dbKeyList.contains(item))
			                       .map(item -> this.insertFunction.apply(dataMap.get(item)))
			                       .filter(ObjectUtil::isNotNull)
			                       .collect(Collectors.toList());
		}

		// data中没有，但db中有
		private List<DB> getDelete() {
			return this.dbKeyList.stream()
			                     .filter(item -> !this.dataKeyList.contains(item))
			                     .map(item -> this.dbMap.get(item))
			                     .collect(Collectors.toList());
		}

		// data中有的，且db中也有
		private List<DB> getUpdate() {
			List<DB> updateList = new ArrayList<>(this.dataKeyList.size());
			for (KEY_DATA key : this.dataKeyList) {
				if (!this.dbKeyList.contains(key)) {
					continue;
				}
				DB db = this.dbMap.get(key);
				Object id = ReflectUtil.getFieldValue(db, idField);
				if (id == null) {
					continue;
				}
				DATA data = this.dataMap.get(key);

				DB update = null;
				if (this.updateBiFunction != null) {
					update = this.updateBiFunction.apply(db, data);
				} else {
					update = db;
				}

				if (update != null) {
					ReflectUtil.setFieldValue(update, idField, id);
					updateList.add(update);
				}
			}

			return updateList;
		}

	}

	@Data
	@NoArgsConstructor
	@AllArgsConstructor
	public static class DataClassify<DB> {

		List<DB> insertList;

		List<Long> deleteIdList;

		List<DB> updateList;

		//@Setter(value = AccessLevel.NONE)
		//@Getter(value = AccessLevel.NONE)
		List<DB> deleteList;

		// 数据库中重复的数据
		List<DB> dbRepeatList = new ArrayList<>();

		// 数据中重复的数据
		//List<DataWrapper> dataRepeatList = new ArrayList<>();

	}

}
